#include <stdlib.h>
#include <stdarg.h>
#include "stdio.h"
#include "string.h"


void text_exit_fun() {
    // 程序运行成功
    // 等同于 exit(0);
//    exit(EXIT_SUCCESS);

    // 程序异常中止
    // 等同于 exit(1);
//    exit(EXIT_FAILURE);
}

/**
 * 函数参数
 * 1. 基本类型 ：值传递(数值、字符、布尔、结构体、共用体、枚举)
 *  2. 指针类型 ：地址传递（数组、指针）
 */
int param_rand() {
    return rand();
}

void param_array(int *array, int arrayLen, int (*f)()) {
    for (int i = 0; i < arrayLen; i++) {
        array[i] = (*f)();
    }

}

void test_parameters() {
//    func6();
    int arrLen = 10;
    int myArray[arrLen];
    param_array(myArray, arrLen, &param_rand);
    //遍历数组
    for (int i = 0; i < 10; i++) {
        printf("%d ", myArray[i]);
    }
    printf("\n");
}

//有返回值
int test_return_value(int m, int n) {
    return m + n;
}

// 无返回值
void test_void_return_value(int x, int y) {
    int z;
    z = x > y ? x : y;
    printf("%d\n", z);
}

// 可变参数，可变参数前必须有一个具名参数，用于定位
void test_var_args(int n, int m, ...) {
    va_list args; // 声明一个va_list变量，存储可变参数
    // 第一个参数：可变参数
    // 第二个参数：可变参数前的参数，用于对可变参数定位
    va_start(args, m); // 初始化va_list，指向可变参数的位置
    // 遍历可变参数
    // va_arg(args, int) 获取可变参数的值，并指向下一个可变参数
    // 第一个参数 ：可变参数
    // 第二个参数 ：可变参数的类型
    for (int i = 0; i < n; i++) {
        printf("%d\n", va_arg(args, int)); // 获取可变参数的值，并指向下一个可变参数
    }
}

// 指针函数
char *test_pointer_function(char *str1, char *str2) {
    printf("\nstr1的长度%lu,str2的长度%lu", strlen(str1), strlen(str2));
    if (strlen(str1) >= strlen(str2)) {
        return str1;
    } else {
        return str2;
    }
}

/*
 * 在 func() 函数中，声明了一个整数变量 n，然后返回其地址 `&n`。
 * 但是，一旦 func() 函数执行完毕，局部变量 n将被销毁，它的地址也将变得无效。
 * 这意味着在 main() 函数中，尝试访问 p 指向的地址时，它实际上已经不再是一个有效的内存位置，这会导致未定义的行为。
 *
 *
 */

int *test_local_var_func() {
    int n = 100;
    return &n;
}

/*
 * 如果确实希望返回局部变量的地址，除了使用malloc()函数的方式之外，
 * 还可以定义局部变量为 `static` 的，此时数据空间在`静态数据区`分配，静态变量在程序的生命周期内都存在，不会像局部变量那样在函数执行完毕后被销毁。
 */

int *test_static_var_func() {
    static int n = 100;
    return &n;
}

// 回调函数
// 指向函数a的指针变量的一个重要用途是把函数a的入口地址作为参数传递到其它函数b中，此时的函数b就称为`回调函数`。在此基础上，我们就可以在回调函数b中使用实参函数a。

int callback_rand() {
    return rand();
}

void callback_func(int *array, int arrayLen, int (*f)()) {
    for (int i = 0; i < arrayLen; i++)
        array[i] = (*f)();
}

void test_callback_func() {
    int arrLen = 10;
    int myArray[arrLen];
    callback_func(myArray, arrLen, &callback_rand);
}


// 函数指针(指向函数的指针) ： 返回值类型 (*指针变量名)(参数列表);
// 一个函数本身就是一段内存里面的代码，总是占用一段连续的内存区域。这段内存区域也有首地址，把函数的这个首地址（或称入口地址）赋予一个指针变量。
void func3() {
    printf("func3\n");
}

int func4() {
    printf("func4\n");
    return 0;
}

int *func5() {
    printf("func5\n");
    static int n = 100;
    return &n;
}

void test_func_pointer() {
    // 函数指针
    void (*p3)() = &func3;// func3是一个函数
    int (*p4)() = &func4;
    int *(*p5)() = &func5;
    printf("p3:%p\n", p3);
    printf("p4:%p\n", p4);
    printf("p5:%p\n", p5);
    (*p3)();
    p3();
    (*p4)();
    p4();
    int *p6 = p5();
    int *p7 = (*p5)();

    // C 语言规定，函数名本身就是指向函数代码的指针，通过函数名就能获取函数地址。也就是说， print 和 &print 是一回事。
    printf("func3() == p3 : \n", func3 == p3);
}


// 内部函数 ，文件内有效
static int test_static_func(int a, int b) {
    printf("f(%d,%d)\n", a, b);
    return a + b;
}

// 外部函数，可以被其他文件使用，默认是外部函数
extern int test_extern_func(int a, int b) {
    printf("f(%d,%d)\n", a, b);
    return a + b;
}

int main() {
    text_exit_fun();
    test_parameters();
    printf("有返回值的函数 ：%d\n", test_return_value(1, 2));
    test_void_return_value(1, 2);
    test_var_args(3, 1, 2, 3, 4);
    printf("指针函数 ：%s\n", test_pointer_function("hello", "world"));
    printf("局部变量指针 ：%p\n", test_local_var_func()); // 报错，因为局部变量已经销毁
    printf("静态变量指针 ：%p\n", test_static_var_func());
    test_callback_func();
    test_func_pointer();
    printf("静态函数 ：%d\n", test_static_func(1, 2));
    printf("外部函数 ：%d\n", test_extern_func(1, 2));


}


/*
    栈内存布局
    +------------------+
    |     栈(stack)    |  ← 局部变量、函数参数
    +------------------+
    |                  |
    |                  |
    |                  |
    +------------------+
    |     堆(heap)     |  ← malloc/new动态分配
    +------------------+
    |    .bss段        |  ← 未初始化静态/全局变量
    +------------------+
    |    .data段       |  ← 已初始化静态/全局变量
    +------------------+
    |    代码段(text)   |  ← 程序指令
    +------------------+
*/